import random
import numpy as np


class DNA_lcq():
    # 初定义
    def __init__(self, ParameterRange, ParameterAccuracy) -> None:
        # 超参数
        # parameter_range:      参数的约束范围（左右闭区间）
        self.ParameterRange = ParameterRange
        self.L = len(ParameterRange)  # 编码总长度

        # 产生一个随机编码
        self.dna_list = [random.uniform(left, right) for left, right in self.ParameterRange]  # 初始随机生成

    # 解码
    def to_nums(self):
        return self.dna_list

    # 检验产生的编码是否合格
    def is_qualified(self) -> bool:
        # 检验是否在范围内
        return all(self.ParameterRange[i][0] <= self.dna_list[i] <= self.ParameterRange[i][1] for i in range(len(self.dna_list)))

    # 变异
    def variation(self):

        # 变异个数， 保证至少变异一个 最多全部变异
        nums = min(self.L, max(1, abs(int(random.gauss(0, self.L // 3)))))
        # 随机产生变异的位置
        sites = random.sample(list(range(0, self.L)), k=nums)
        # 开始变异
        ans = DNA_lcq(self.ParameterRange, None)
        ans.dna_list = self.dna_list[::1]
        for site in sites:
            ans.dna_list[site] = random.uniform(*self.ParameterRange[site])

        while not ans.is_qualified():
            # 变异个数， 保证至少变异一个 最多全部变异
            nums = min(self.L, max(1, abs(int(random.gauss(0, self.L // 3)))))  # 产生一个随机数
            # 随机产生变异的位置
            sites = random.sample(list(range(0, self.L)), k=nums)
            # 开始变异
            ans = DNA_lcq(self.ParameterRange, None)
            ans.dna_list = self.dna_list[::1]
            for site in sites:
                ans.dna_list[site] = random.uniform(*self.ParameterRange[site])
        return ans

    # 交叉 (线性交叉)
    def crossover(self, other):
        # 两个随机数
        y1 = random.random()
        y2 = random.random()
        ans = DNA_lcq(self.ParameterRange, None)
        # 线性求和
        ans.dna_list = [num1 * y1 + num2 * y2 for num1, num2 in zip(self.dna_list, other.dna_list)]

        while not ans.is_qualified():
            # 两个随机数
            y1 = random.random()
            y2 = random.random()
            ans = DNA_lcq(self.ParameterRange, None)
            # 线性求和
            ans.dna_list = [num1 * y1 + num2 * y2 for num1, num2 in zip(self.dna_list, other.dna_list)]
        return ans

    # 个体间的差异比较(如果两个个体过于相似返回false，但是为了适应我的代码，当和自己比较时需要返回true)
    def get_difference(self, other):
        ans = 0
        for a, b in zip(self.dna_list, other.dna_list):
            ans += abs(a - b)
    
        return ans > 0.3 or ans < 0.0001

    # 打印
    def __str__(self) -> str:
        return str(self.to_nums())

if __name__ == "__main__":

    n = 10  # 尺寸
    left = 1  # 下限（闭区间）
    right = 4  # 上限 （闭区间）
    a = DNA_lcq([[left, right]] * n, None)  # 精度不管
    print("第一个DNA")
    print(a)
    b = DNA_lcq([[left, right]] * n, None)  # 精度不管
    print("第二个DNA")
    print(b)

    print("它们交叉")
    print(a.crossover(b))

    print("第一个变异")
    print(a.variation())